#include "WidgetInspector.h"
#include "ui_WidgetInspector.h"
#include "WidgetInspectorUi.h"

#include <Windows.h>
#include <WinUser.h>
#pragma comment(lib, "User32.lib")

#include "wingdi.h"
#pragma comment(lib, "Gdi32.lib")

#include <QApplication>
#include <QTimer>
#include <QShortcut>

#include <QDebug>
#define DEBUG qDebug().noquote() << __FUNCTION__

QString ColorName(const QColor& color, WidgetInspector::ColorNameFormat format = WidgetInspector::ColorNameFormat::HexRgb) {
    if (format == WidgetInspector::ColorNameFormat::HexRgb) {
        return color.name(QColor::HexRgb);
    } 
    
    return QString("%1, %2, %3").arg(color.red()).arg(color.green()).arg(color.blue());
}

struct WidgetInspector::WidgetInspectorPrivate {
    QPointer<QWidget> widget;
    QPointer<QWidget> displayWidget;
    QList<WidgetIndicator*> indicators;
    bool pickEnabled = false;
    QColor color;
};

QColor GetColorUnderCursor() {
    POINT cursorPos;
    ::GetCursorPos(&cursorPos);
    HDC hdc = ::GetDC(NULL);
    COLORREF colorRef = ::GetPixel(hdc, cursorPos.x, cursorPos.y);
    ::ReleaseDC(NULL, hdc);
    return QColor(GetRValue(colorRef), GetGValue(colorRef), GetBValue(colorRef));
}

WidgetInspector::~WidgetInspector() { }

WidgetInspector::WidgetInspector(QWidget *parent) : QWidget(parent) {
    ui.reset(new WidgetInspectorUi);
    ui->SetupUI(this);

    p.reset(new WidgetInspectorPrivate);

    setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);

    const auto timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &WidgetInspector::OnTick);
    timer->start(1000/20);

    const auto shortCutShowOrHide = new QShortcut(parentWidget());
    shortCutShowOrHide->setKey(QKeySequence(Qt::ALT + Qt::Key_H));
    shortCutShowOrHide->setContext(Qt::ApplicationShortcut);
    connect(shortCutShowOrHide, &QShortcut::activated, this, [=] { setVisible(!isVisible()); });

    const auto shortCut = new QShortcut(parentWidget());
    shortCut->setKey(QKeySequence(Qt::ALT + Qt::Key_I));
    shortCut->setContext(Qt::ApplicationShortcut);
    connect(shortCut, &QShortcut::activated, this, [=] { p->pickEnabled = !p->pickEnabled; });

    ui->comboboxColorFormat->addItem("HexRGB", QVariant::fromValue(ColorNameFormat::HexRgb));
    ui->comboboxColorFormat->addItem("RGB", QVariant::fromValue(ColorNameFormat::Rgb));
    ui->comboboxColorFormat->setCurrentText("HexRGB");

    qApp->installEventFilter(this);
}

void WidgetInspector::OnTick() {
    if(p->pickEnabled) {
        const auto nouseOver = rect().contains(mapFromGlobal(QCursor::pos()));
        if(!nouseOver) {
            PickWidget();
        }
    }

    if(p->displayWidget) {
        RefreshWidgetProperties();
    }
}

void WidgetInspector::PickWidget() {
    const auto &cursorPos = QCursor::pos();
    const auto widget = QApplication::widgetAt(cursorPos);
    if(widget != nullptr) {
        SetCurrentWidget(widget);
    }
}

void WidgetInspector::SetCurrentWidget(QWidget *widget) {
    if(p->widget == widget) {
        return ;
    }
    p->widget = widget;
    p->displayWidget = widget;

    qDeleteAll(p->indicators);
    p->indicators.clear();
    auto parentWidget = widget;
    while(parentWidget != nullptr) {
        const auto &parentClassName = parentWidget->metaObject()->className();
        const auto &parentObjectName = parentWidget->objectName();
        const auto &text =  QString("%1:%2").arg(parentClassName).arg(parentObjectName);
        
        const auto indicator = new WidgetIndicator;
        indicator->setText(text);
        indicator->SetWidget(parentWidget);
        indicator->setChecked(parentWidget == p->displayWidget);
        p->indicators.append(indicator);
        ui->layoutObjectHierarchyContents->addWidget(indicator);
        connect(indicator, &WidgetIndicator::clicked, this, [=] {
            if(indicator->isChecked()) {
                p->displayWidget = indicator->Widget();
                RefreshWidgetProperties();
            }
        });

        parentWidget = parentWidget->parentWidget();
    }
}

QString ToString(const QSize &size) {
    return QString("%1, %2").arg(size.width()).arg(size.height());
}

QString ToString(const QPoint &point) {
    return QString("%1, %2").arg(point.x()).arg(point.y());
}


void WidgetInspector::RefreshWidgetProperties() {
    if (!p->displayWidget) return;
    const auto &widget = *(p->displayWidget.data());

    QStringList inheritList;
    auto metaObject = widget.metaObject();
    while(metaObject != nullptr) {
        const auto &className = metaObject->className();
        inheritList.append(className);
        metaObject = metaObject->superClass();
    }

    const auto CanRefresh = [=](QWidget* controll) {
        const auto &mouseOver = controll->rect().contains(controll->mapFromGlobal(QCursor::pos()));
        const auto &hasFocus = controll->hasFocus();
        bool can_fresh = !hasFocus;
        return can_fresh;
    };

    if (CanRefresh(ui->editObjectName)) ui->editObjectName->setText(widget.objectName());
    if (CanRefresh(ui->editClassName)) ui->editClassName->setText(widget.metaObject()->className());
    if (CanRefresh(ui->editInheritHierarchy)) ui->editInheritHierarchy->setText(inheritList.join(" -> "));

    if (CanRefresh(ui->editPosition)) ui->editPosition->setText(::ToString(widget.pos()));
    if (CanRefresh(ui->editSize)) ui->editSize->setText(::ToString(widget.size()));
    if (CanRefresh(ui->editStylesheet)) ui->editStylesheet->setPlainText(widget.styleSheet());

    p->color = GetColorUnderCursor();
    if(CanRefresh(ui->editUnderCursorColor)) {
        const auto &nameFormat = ui->comboboxColorFormat->currentData().value<ColorNameFormat>();
        const auto& colorText = ::ColorName(p->color, nameFormat);
        ui->editUnderCursorColor->setText(colorText.toUpper());
    }
}

void WidgetInspector::AppendMessage(const QString &message) {
    ui->editDebugMessage->append(message);
}

bool WidgetInspector::eventFilter(QObject *watched, QEvent *event) {
    const bool result = QWidget::eventFilter(watched, event);

    if(event->type() == QEvent::WinIdChange) {      
        QWidget *widget = qobject_cast<QWidget*>(watched);
        if (nullptr != widget && widget->isWindow()) {
            const auto& class_name = watched->metaObject()->className();
            const QString& object_name = watched->objectName();
            const auto& message = QString("%1:: 类名:%2, 对象名:%3, ID:%4")
                                      .arg("窗口ID改变")
                                      .arg(class_name)
                                      .arg(object_name.isEmpty() ? "\"\"" : object_name)
                                      .arg(size_t(watched), 16, 16, QChar('0')).arg(window()->winId());
            AppendMessage(message);
        }     
    }

    return result;
}

QSize WidgetInspector::ScrollArea::sizeHint() const {
    auto hint = QScrollArea::sizeHint();
    DEBUG << hint;
    if(widget()) {
        hint.rheight() = 28;
    } else {
        hint.rheight() = 28;
    }
    DEBUG << hint;
    return hint;
}

QSize WidgetInspector::ScrollArea::minimumSizeHint() const {
    auto hint = QScrollArea::minimumSizeHint();
    DEBUG << hint;
    if(widget()) {
        hint.rheight() = 28;
    } else {
        hint.rheight() = 28;
    }
    DEBUG << hint;
    return hint;
}
